package org.sp.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * @author ShouPeng
 * @date 2020-11-22
 * 二维码工具类
 *
 * pom
 *
 *         <!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
 *         <dependency>
 *             <groupId>com.google.zxing</groupId>
 *             <artifactId>core</artifactId>
 *             <version>3.3.3</version>
 *         </dependency>
 *
 *         <!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
 *         <dependency>
 *             <groupId>com.google.zxing</groupId>
 *             <artifactId>javase</artifactId>
 *             <version>3.3.3</version>
 *         </dependency>
 */
public class QrCodeUtil {

    private final static QrCodeUtil QR_CODE_UTIL;

    /**
     * 默认生成文件类型
     */
    private final static String GENERATE_IMG_TYPE = "png";

    /**
     * 默认生成文件大小
     */
    private final static Integer GENERATE_IMG_SIZE = 200;

    /**
     * 生成的二维码边距
     */
    private static Integer GENERATE_IMG_MARGIN = 1;

    static {
        QR_CODE_UTIL = new QrCodeUtil();
    }

    /**
     * 二维码解码
     * @param qr
     * @return
     */
    public static String decodeQr(File qr){
        return QR_CODE_UTIL.decodeImg(qr);
    }

    /**
     * 二维码解码
     * @param qr
     * @return
     */
    public static String decodeQr(String qr){
        if (null == qr || qr.isEmpty()){
            throw new RuntimeException("文件不能为空");
        }
        return QR_CODE_UTIL.decodeImg(new File(qr));
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     */
    public static void generate(String content,File file){
        QR_CODE_UTIL.encodeImt(content,file,GENERATE_IMG_TYPE,GENERATE_IMG_SIZE,null);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     */
    public static void generate(String content,String file){
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,GENERATE_IMG_SIZE,null);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     */
    public static void generate(String content,String file,Integer size){
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,size,null);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     * @param margin 边距
     */
    public static void generate(String content,String file,Integer size,Integer margin){
        GENERATE_IMG_MARGIN = margin;
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,size,null);
        GENERATE_IMG_MARGIN = 1;
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     */
    public static void generate(String content,File file,Integer size){
        QR_CODE_UTIL.encodeImt(content,file,GENERATE_IMG_TYPE,size,null);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     * @param margin 边距
     */
    public static void generate(String content,File file,Integer size,Integer margin){
        GENERATE_IMG_MARGIN = margin;
        QR_CODE_UTIL.encodeImt(content,file,GENERATE_IMG_TYPE,size,null);
        GENERATE_IMG_MARGIN = 1;
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     * @param log 二维码log
     */
    public static void generate(String content,String file,Integer size,String log){
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,size,new File(log));
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     * @param log 二维码log
     */
    public static void generate(String content,String file,Integer size,File log){
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,size,log);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param size 文件大小
     * @param log 二维码log
     * @param margin 边距
     */
    public static void generate(String content,String file,Integer size,File log,Integer margin){
        GENERATE_IMG_MARGIN = margin;
        QR_CODE_UTIL.encodeImt(content,new File(file),GENERATE_IMG_TYPE,size,log);
        GENERATE_IMG_MARGIN = 1;
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param imgType 文件类型
     * @param size 文件大小
     */
    public static void generate(String content,String file,String imgType,Integer size){
        QR_CODE_UTIL.encodeImt(content,new File(file),imgType,size,null);
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     * @param imgType 文件类型
     * @param size 文件大小
     * @param log 二维码log
     */
    public static void generate(String content,String file,String imgType,Integer size,String log){
        QR_CODE_UTIL.encodeImt(content,new File(file),imgType,size,new File(log));
    }

    /**
     * 产生二维码
     * @param content 内容
     * @param file 生成文件
     */
    public static void generate(String content,String file,String imgType,Integer size,File log){
        QR_CODE_UTIL.encodeImt(content,new File(file),imgType,size,log);
    }



    /**
     * 加密: 文字->二维码
     */
    private void encodeImt(String content,File file,String imgType,int size,File logo){

        try {
            //content:需要加密的文字
            //BarcodeFormat.QR_CODE:解密的类型 ->>二维码
            Map<EncodeHintType,Object> map = new Hashtable<>();
            //设置容错率 L<M<Q<H
            map.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.Q);
            //编码
            map.put(EncodeHintType.CHARACTER_SET,"UTF-8");

            //设置外边距
            map.put(EncodeHintType.MARGIN,QrCodeUtil.GENERATE_IMG_MARGIN);
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE,size,size,map);

            //先生成内存中的图片，此时需要的图片是二维码 -> 需要Boolean[][]
            // 然后在写入内存中
            BufferedImage image = new BufferedImage(size,size,BufferedImage.TYPE_INT_RGB);

            for (int x = 0;x < size;x++){
                for (int y = 0;y < size;y++){
                    image.setRGB(x,y,bitMatrix.get(x,y) ? Color.BLACK.getRGB():Color.WHITE.getRGB());
                }
            }

            //画logo
            BufferedImage logoImg = logoMatrix(image, logo);

            //生成二维码图片
            ImageIO.write(logoImg,imgType,file);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (WriterException e) {
                e.printStackTrace();
        }
    }

    /**
     * 传入logo、二维码、--》》带logo的二维码
     */
    private BufferedImage logoMatrix(BufferedImage matrix, File logo){
        //在二维码上画logo:产生一个二维码画板
        if(matrix == null || logo == null){
            return matrix;
        }

        Graphics2D g2 = matrix.createGraphics();

        try {

            //画logo :String -> BufferedImage（内存）
            BufferedImage logoImg = ImageIO.read(logo);
            int height = matrix.getHeight();
            int width = matrix.getWidth();
            //纯logo
            g2.drawImage(logoImg,width*2/5,height*2/5,width/5,height/5,null);
            //产生一个画笔
            //画白色圆角的正方形的画笔
            BasicStroke stroke = new BasicStroke(3,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
            //将画板与画笔关联
            g2.setStroke(stroke);
            RoundRectangle2D.Float round = new RoundRectangle2D.Float(width*2/5,height*2/5,width/5,height/5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
            g2.setColor(Color.WHITE);
            g2.draw(round);

            //灰色边框的画笔
//            BasicStroke stroke2 = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
//            g2.setStroke(stroke2);
//            RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(width*2/5 + 1,height*2/5 + 1,width/5,height/5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
//            g2.setColor(Color.GRAY);
//            g2.draw(round);

            g2.dispose();
            matrix.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return matrix;
    }

    /**
     * 解码
     * @param file
     * @return
     */
    private String decodeImg(File file){
        if(!file.exists()){
            return "file does not exist！！！";
        }

        try {
            //file -> 内存中的图片
            BufferedImage read = ImageIO.read(file);
            MultiFormatReader formatReader = new MultiFormatReader();

            LuminanceSource source = new BufferedImageLuminanceSource(read);

            Binarizer binarizer = new HybridBinarizer(source);

            BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);

            //图片 ->result
            Map<DecodeHintType,Object> map = new HashMap<>(1);
            map.put(DecodeHintType.CHARACTER_SET,"UTF-8");
            Result result = formatReader.decode(binaryBitmap,map);

            return result.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return "io error";
        } catch (NotFoundException e) {
            e.printStackTrace();
            return "file not found error";
        }
    }
}
